#include <vga.h>
#include <system.h>

unsigned short *textmemptr; //Pointer to the start of the text mode memory buffer
unsigned char colors;
unsigned short csr_x;
unsigned short csr_y;
int cursor;

void vga_init(void) {
   textmemptr = (unsigned short *)0xB8000;
   vga_setcolors(0,15);
   vga_setcursor(1);
   vga_clear();
}

void vga_setcursor(int c) {
   unsigned char cursor_start_reg;
   unsigned char max_scan_line;

   outportb(0x3D4,0x0A);
   cursor_start_reg = inportb(0x3D5);
   outportb(0x3D4,0x09);
   max_scan_line = inportb(0x3D5) & 31;
   if (c == 0) {
      outportb(0x3D4,0x0A);
      cursor_start_reg = cursor_start_reg | 16;
      outportb(0x3D5,cursor_start_reg);
   } else {
      cursor = 1;
      outportb(0x3D4,0x0A);
      cursor_start_reg = cursor_start_reg & 16;
      cursor_start_reg = cursor_start_reg & ~31;
      cursor_start_reg = cursor_start_reg | (max_scan_line-1);
      outportb(0x3D5,cursor_start_reg);
   }
}

void update_cursor() {
   unsigned short temp;

   temp = csr_y * 80 + csr_x;

   outportb(0x3D4,0x0E);
   outportb(0x3D5,temp>>8);
   outportb(0x3D4,0x0F);
   outportb(0x3D5,temp);
}

void vga_clear() {
   unsigned short blank;
   int i;

   csr_x = 0;
   csr_y = 0;

   blank = 0x20 | (colors << 8); // A space set to the current colors

   for (i=0; i<25; i++)
      memsetw(textmemptr + i*80, blank, 80);

   if (cursor) update_cursor();
}

void vga_scroll() {
   unsigned short blank;
   unsigned char *sourceptr;
   unsigned char *lastline;

   blank = 0x20 | (colors << 8); // A space set to the current colors

   sourceptr = ((unsigned char *)textmemptr) + 160;

   memcpy((unsigned char *)textmemptr,sourceptr,24*160);

   lastline = ((unsigned char *)textmemptr) + 24*160;
   memsetw((unsigned short *)lastline, blank, 80);

   csr_y = 24;
}

void vga_put(char c) {
   unsigned short *dest;
   
   // Handle various characters. Note the cursor may become invalid,
   // but we'll fix that after the switch
   switch (c) {
      // backspace
      case 0x08: csr_x--;
                 break;
      // tab
      case 0x09: csr_x = (csr_x + 4) & ~(8 - 1);
                 break;
      // carriage return
      case '\r': csr_x = 0;
                 break;
      case '\n': csr_x = 0;
                 csr_y++;
                 break;
      default:   dest = textmemptr + (csr_y*80 + csr_x); // Compute the mem location
                 *dest = c | (colors << 8);              // Write the character + color data
                 csr_x++;
   }

   // Fix our cursor location and scroll if necessary
   if (csr_x >= 80) {
      csr_x = 0;
      csr_y++;
   }
   if (csr_y >= 25) {
      vga_scroll();
   }

   if (cursor) update_cursor();
}

int vga_puts(char *str) {
   int i;
   while (*str != 0) {
      vga_put(*str);
      str++;
      i++;
   }
   return i;
}

void vga_setcolors(unsigned char bg, unsigned char fg) {
   colors = (bg << 4) | (fg & 0x0F);
}
